﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;

namespace CSW.Framework.Linq.SchemaModel
{
    /// <summary>
    /// Table model.
    /// </summary>
    public class Table
    {
        private readonly List<TableColumn> m_Columns = new List<TableColumn>();
        private readonly BindingList<TableProperty> m_Properties = new x();
        private readonly List<UniqueConstraint> m_UniqueConstraints = new List<UniqueConstraint>();
        private string m_TableName;

        private class x : BindingList<TableProperty>
        {
            protected override void InsertItem(int index, TableProperty item)
            {
                TableProperty dupe = this.Where(p => p != item && p.PropertyName == item.PropertyName).SingleOrDefault();
                if (dupe != null)
                {
                    throw new Exception(string.Format("Duplicate property name {0}.", item.PropertyName));
                }

                base.InsertItem(index, item);
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Table"/> class.
        /// </summary>
        public Table()
        {
            //m_Properties.ListChanged += m_Properties_ListChanged;
        }

        private void m_Properties_ListChanged(object sender, ListChangedEventArgs e)
        {
        }

        /// <summary>
        /// Gets or sets the schema.
        /// </summary>
        /// <value>The schema.</value>
        public string Schema { get; set; }

        /// <summary>
        /// Gets a value indicating whether this table has computed columns.
        /// </summary>
        /// <value><c>true</c> if this table has computed columns; otherwise, <c>false</c>.</value>
        public bool HasComputedColumns
        {
            get 
            {
                foreach (TableColumn column in m_Columns)
                {
                    if (column.IsComputed)
                        return true;
                }
                return false;
            }
        }

        /// <summary>
        /// Gets or sets the name of the table.
        /// </summary>
        /// <value>The name of the table.</value>
        public string TableName 
        {
            get { return m_TableName; }
            set
            {
                m_TableName = value;
                if (string.IsNullOrEmpty(ClassName))
                {
                    // removed for SqlCe
                    //if (string.IsNullOrEmpty(Schema))
                    //    throw new Exception("Schema not set.");

                    if (Schema == "dbo" || string.IsNullOrEmpty(Schema))
                        ClassName = value;
                    else
                        ClassName = string.Format("{0}_{1}", Schema, value);
                }
            }
        }

        /// <summary>
        /// Gets the name of the class.
        /// </summary>
        /// <value>The name of the class.</value>
        public string ClassName { get; set; }

        /// <summary>
        /// Gets the full name of the class.
        /// </summary>
        /// <value>The full name of the class.</value>
        public string FullClassName { get; set; }

        /// <summary>
        /// Gets the columns.
        /// </summary>
        /// <value>The columns.</value>
        public IList<TableColumn> Columns { get { return m_Columns; } }

        /// <summary>
        /// Gets the properties.
        /// </summary>
        /// <value>The properties.</value>
        public IList<TableProperty> Properties { get { return m_Properties; } }

            /// <summary>
        /// Gets the unique constraints.
        /// </summary>
        /// <value>The unique constraints.</value>
        public IList<UniqueConstraint> UniqueConstraints { get { return m_UniqueConstraints; } }

        /// <summary>
        /// Gets the primary key.
        /// </summary>
        /// <value>The primary key.</value>
        public TableColumn[] PrimaryKey
        {
            get 
            {
                List<TableColumn> pk = new List<TableColumn>();
                foreach (TableColumn column in m_Columns)
                {
                    if (column.IsKey)
                        pk.Add(column);
                }
                return pk.ToArray();
            }
        }

        /// <summary>
        /// Gets the primary key.
        /// </summary>
        /// <value>The primary key.</value>
        public TableColumn[] UniqueColumns
        {
            get
            {
                List<TableColumn> uq = new List<TableColumn>(PrimaryKey);
                foreach (UniqueConstraint uqConst in UniqueConstraints)
                {
                    foreach (TableColumn uqCol in uqConst.Columns)
                    {
                        if (!uq.Contains(uqCol))
                            uq.Add(uqCol);
                    }
                }
                return uq.ToArray();
            }
        }

        /// <summary>
        /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </returns>
        public override string ToString()
        {
            return string.Format("{0}.{1}", Schema, TableName);
        }

        /// <summary>
        /// Gets a unique name for a property about to be added to the Properties list.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        /// <returns>A unique name.</returns>
        public string GetUniquePropertyName(string propertyName)
        {
            return GetUniquePropertyName(null, propertyName);
        }

        /// <summary>
        /// Gets a unique name for a property about to be added to the Properties list.
        /// </summary>
        /// <param name="property">The property.</param>
        /// <param name="propertyName">Name of the property.</param>
        /// <returns>A unique name.</returns>
        public string GetUniquePropertyName(TableProperty property, string propertyName)
        {
            propertyName = propertyName.Replace(" ", "_").Replace("#", "Number").Replace("/", "_");
            if (!char.IsLetter(propertyName[0]) && propertyName[0] != '_')
            {
                propertyName = "_" + propertyName;
            }

            if (propertyName == ClassName)
            {
                propertyName += "_1";
            }

            while (Properties.Where(p => p.PropertyName == propertyName && p != property).Any())
            {
                propertyName += "_1";
            }

            return propertyName;
        }
    }
}